/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.nViron.Graffiti;

import instar.dg.Protocol.Messages.WLANPosAvailable;
import java.io.IOException;
import java.io.Serializable;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import instar.dg.Client.BaseClient;
import instar.dg.Client.ClientListener;
import instar.dg.Client.ClientProfile;
import instar.dg.Client.Me;
import instar.dg.Client.BaseClient.ClientProviderFacade;
import instar.dg.Common.Error;
import instar.dg.Common.Position;
import instar.dg.Common.Version;
import instar.dg.GraffitiCache.GraffitiCache;
import instar.dg.GraffitiCache.PersistentCacheMemory;
import instar.dg.Graffito.GraffitoBody;
import instar.dg.Graffito.GraffitoHeader;
import instar.dg.Packets.Channel;
import instar.dg.Peer.Proxy;
import instar.dg.Positioning.PositionProvider;
import instar.dg.Profile.AddressBook;
import instar.dg.Protocol.Replies.ErrorReply;
import instar.dg.Protocol.Requests.Request;
import instar.dg.SpatialCoding.SpatialNameList;

import java.net.ConnectException;
import org.nViron.client.Config;
import org.nViron.client.Debug;

public class GraffitiClient extends BaseClient implements Serializable, ClientListener {

    private static final long serialVersionUID = -3951408843932341341L;
    private Error error;
    private String serverIP;
    private int serverPort;
    private final GraffitiSource graffitiSource;

    public GraffitiClient(GraffitiSource graffitiSource, Config config) {
        super();

        this.graffitiSource = graffitiSource;

        getProviderPeer().setFacade(new ClientProviderFacadeDesktopClient(this));

        setMe(new Me());
        setGraffitiCache(new GraffitiCache(this, new PersistentCacheMemory(this, 1000, 1000)));
        getMe().setProfile(new ClientProfile(new AddressBook(this)));

        setPosition(new Position(config.getUserPos().getLongitude(), config.getUserPos().getLatitude(), config.getUserPos().getAltitude()));
        setPerceptualPosition(me.getPosition());

        //set listeners
        addClientListener(this);
    }

    @Override
    public void setState(byte _State) {
        super.setState(_State);
        if (_State == READY) {
            graffitiSource.setID(me.getId());
        }
    }

    public void doLogin(String username, String password) {
        getMe().setNickname(username);
        getMe().setPassword(password);

        byte ok = start();

        try {
            synchronized (this) {
                Debug.out.println("B4 wait");
                this.wait();
                Debug.out.println("After wait");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    protected byte connect(String _serverIP, int _serverPort) {
        serverIP = _serverIP;
        serverPort = _serverPort;
        return connect();
    }

    @Override
    protected byte connect() {
        try {
            Socket sock = new java.net.Socket(serverIP, serverPort);
            Channel channel = new Channel(sock.getInputStream(), sock.getOutputStream());
            Proxy proxy = getProviderPeer().addConnection(channel);

        } catch (UnknownHostException ex) {
            Logger.getLogger(GraffitiClient.class.getName()).log(Level.SEVERE, null, ex);
            return ERROR_PROVIDER;
        } catch (IOException ex) {
            Logger.getLogger(GraffitiClient.class.getName()).log(Level.SEVERE, null, ex);
            return ERROR_PROVIDER;
        }
        return NO_ERROR;
    }

    @Override
    public void disconnect() {
        getProviderPeer().disconnect();
    }

    @Override
    public String getClientName() {
        return "nViron";
    }

    @Override
    public String getClientUpdateIdentification() {
        return "java.web";
    }

    @Override
    public String getClientVendor() {
        return "Roland Haring";
    }

    @Override
    public Version getClientVersion() {
        return new Version(1, 5, 0);
    }

    /********************  CLIENT LISTENER   ***************************/
    @Override
    public void onClientUpdateNotification(String arg0, String arg1) {
        Debug.out.println("Client Update " + arg0 + " " + arg1);
    }

    @Override
    public void onError(Error error) {
        Debug.out.println("Error " + error.getErrorMessage() + " " + error.getErrorCode() + " " + error.getErrorCodeDescription());
    }

    @Override
    public void onFriendFinderPermissionStateReply() {
    }

    @Override
    public void onFriendFinderPermissionUpdateReply() {
    }

    @Override
    public void onGraffitoCreation(GraffitoHeader arg0, GraffitoBody arg1) {
    }

    @Override
    public void onIncomingBody(GraffitoHeader arg0, GraffitoBody arg1) {
    }

    @Override
    public void onStateChanged(byte oldState, byte newState) {
        Debug.out.println("onstatechanged reached");
        switch (newState) {
            case BaseClient.STARTED:
                Debug.out.println("STATE started");
                break;
            case BaseClient.CONNECTED:
                Debug.out.println("STATE connected");
                break;
            case BaseClient.READY:
                Debug.out.println("STATE ready");
                sendAlive();
                break;
            default:
                Debug.out.println("STATE else: " + newState);
        }

        synchronized (this) {
            this.notifyAll();
            Debug.out.println("Notified");
        }
    }

    @Override
    public void WLANPositioningAvailable(WLANPosAvailable msg) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    class ClientProviderFacadeDesktopClient extends ClientProviderFacade {

        private GraffitiClient client;

        public ClientProviderFacadeDesktopClient(GraffitiClient client) {
            super();
            this.client = client;
        }

        @Override
        public void replyError(Request request, ErrorReply errorReply) throws IOException {

            error = errorReply.getError();

            synchronized (client) {
                client.notifyAll();
                Debug.out.println("Notified");
            }

            super.replyError(request, errorReply);
        }
    }

    public Error getError() {
        return error;
    }

    public void setError(Error error) {
        this.error = error;
    }

    @Override
    public void positionSystemChanged(PositionProvider arg0, PositionProvider arg1) {
    }

    @Override
    public void spatialNameReply(SpatialNameList list) {
    }
}
